﻿

using MockingBird.Simulator.Framework;

namespace MockingBird.Simulator.FunctionalTests.EngineTests
{

    using System;
    using System.IO;
    using System.Xml;
    //Gallio and MbUnit Framework
    using MbUnit.Framework;
    using MockingBird.Common.Infrastructure.Logging;
    using MockingBird.Simulator.Framework.Configuration;
    using MockingBird.Simulator.Framework.Interfaces;
    using MockingBird.Simulator.FunctionalTests.Common;
    using Moq;


    [TestFixture]
    public class SoapHeaderBuilderFixture
    {
        #region file based tests
        private string testFilePath = string.Empty;
        private string testFileName = string.Empty;

        /// <summary>
        /// Expect null response where no soap header config exists
        /// </summary>
        [Test]
        public void NullResponseIfConfigMissing()
        {
            var logger = new Mock<ILogger>(MockBehavior.Default);
            var context = new Mock<IExecutionContext>();
            var appConfig = TestHelper.GetAppConfigData();

            var headersProc = new SoapHeaderBuilder(context.Object, logger.Object, appConfig);

            Assert.IsNull(headersProc.ResponseSoapHeaders(null));
        }

        /// <summary>
        /// Expect exception where config file path is invalid
        /// </summary>
        [Test]
        public void ExceptionIfResponseFilePathInvalid()
        {
            var logger = new Mock<ILogger>(MockBehavior.Default);

            var context = new Mock<IExecutionContext>();
            context.Setup(x => x.EndpointName).Returns("");

            var appConfig = TestHelper.GetAppConfigData();

            var headersProc = new SoapHeaderBuilder(context.Object, logger.Object, appConfig);

            var requestConfig = new RequestSoapHeadersType();
            requestConfig.SetResponseHeaderFromThisFilePath = "yuiop";

            Assert.Throws<InvalidOperationException>(() => headersProc.ResponseSoapHeaders(requestConfig));
        }

        /// <summary>
        /// Expect exception where config file path exists but file is not xml
        /// </summary>
        [Test]
        public void ExceptionIfResponseFileInvalid()
        {
            var logger = new Mock<ILogger>(MockBehavior.Default);

            var context = new Mock<IExecutionContext>();
            context.Setup(x => x.EndpointName).Returns("");

            var appConfig = TestHelper.GetAppConfigData();

            testFilePath = appConfig.EndpointsRoot + @"\responses";

            testFileName = Path.GetFileName(Path.GetTempFileName());
            bool xml = false;
            SetupTestFile(xml);

            var headersProc = new SoapHeaderBuilder(context.Object, logger.Object, appConfig);

            var requestConfig = new RequestSoapHeadersType();
            requestConfig.SetResponseHeaderFromThisFilePath = testFileName;

            Assert.Throws<FileLoadException>(() => headersProc.ResponseSoapHeaders(requestConfig));
        }

        [Test]
        public void ValidHeaderFile()
        {
            var logger = new Mock<ILogger>(MockBehavior.Default);

            var context = new Mock<IExecutionContext>();
            context.Setup(x => x.EndpointName).Returns("");

            var appConfig = TestHelper.GetAppConfigData();

            testFilePath = appConfig.EndpointsRoot + @"\responses";

            testFileName = Path.GetFileName(Path.GetTempFileName());
            bool xml = true;
            SetupTestFile(xml);

            var headersProc = new SoapHeaderBuilder(context.Object, logger.Object, appConfig);

            var requestConfig = new RequestSoapHeadersType();
            requestConfig.SetResponseHeaderFromThisFilePath = testFileName;

            System.Xml.XmlDocument response = headersProc.ResponseSoapHeaders(requestConfig);

            Assert.IsNotNull(response);
            Assert.IsTrue(response.DocumentElement.LocalName.Equals("SomeXml"));
        }

        private void SetupTestFile(bool xml)
        {
            if (!Directory.Exists(testFilePath))
            {
                Directory.CreateDirectory(testFilePath);
            }
            using (StreamWriter sw = new StreamWriter(testFilePath + @"\" + testFileName, false))
            {
                if (xml)
                {
                    sw.WriteLine("<SomeXml/>");
                }
                else
                {
                    sw.WriteLine("Not xml");
                }
                sw.Close();
            }
            return;
        }
        [TearDown]
        private void CleanUpTestFile()
        {
            if (File.Exists(testFilePath + @"\" + testFileName))
            {
                File.Delete(testFilePath + @"\" + testFileName);
            }
        }

        #endregion

        #region config based tests

        /// <summary>
        /// Test that headers provided as in line xml make it back out (to be added into the response envelope by the handler)
        /// </summary>
        [Test]
        public void TestXmlOnlyAndNoXpaths()
        {
            var logger = new Mock<ILogger>(MockBehavior.Default);
            var context = new Mock<IExecutionContext>();
            context.Setup(x => x.EndpointName).Returns("");
            var appConfig = TestHelper.GetAppConfigData();

            // Setup test data

            bool xmlOnly = true;
            RequestSoapHeadersType config = GetConfig(xmlOnly);

            // Construct builder (request is not required here)
            var headersProc = new SoapHeaderBuilder(context.Object, logger.Object, appConfig);

            // Get response
            XmlDocument header = headersProc.ResponseSoapHeaders(config);

            // Assert

            Assert.IsNotNull(header);
            Assert.IsNotNull(header.DocumentElement);

            Assert.IsTrue(header.DocumentElement.LocalName.Equals("Header"));

            XmlNode n = header.DocumentElement.SelectSingleNode("*[local-name()='ResponseHeaderName1']");
            Assert.IsNotNull(n);
            n = header.DocumentElement.SelectSingleNode("*[local-name()='ResponseHeaderName2']");
            Assert.IsNotNull(n);
        }


        /// <summary>
        /// If nothing is matched in the request, then no header can be built, so nothing is passed out
        /// </summary>
        [Test]
        public void XpathsAndNoMatchingRequestHeader()
        {
            var logger = new Mock<ILogger>(MockBehavior.Default);
            var context = new Mock<IExecutionContext>();
            context.Setup(x => x.EndpointName).Returns("");
            var appConfig = TestHelper.GetAppConfigData();

            // Setup test data

            bool xmlOnly = false;
            RequestSoapHeadersType config = GetConfig(xmlOnly);

            bool withHeader = true;
            string requestHeaderName = "UnknownRequestHeader";
            XmlDocument request = GetRequest(withHeader, requestHeaderName);

            // Construct builder
            var headersProc = new SoapHeaderBuilder(context.Object, logger.Object, appConfig, request);

            // Get response
            XmlDocument header = headersProc.ResponseSoapHeaders(config);

            // Assert
            Assert.IsNull(header);

        }

        [Test]
        public void TestXmlWithValuesFromMatchedRequest()
        {
            var logger = new Mock<ILogger>(MockBehavior.Default);
            var context = new Mock<IExecutionContext>();
            context.Setup(x => x.EndpointName).Returns("");
            var appConfig = TestHelper.GetAppConfigData();

            // Setup test data

            bool xmlOnly = false;
            RequestSoapHeadersType config = GetConfig(xmlOnly);

            bool withHeader = true;
            string requestHeaderName = "RequestHeaderName1";
            XmlDocument request = GetRequest(withHeader, requestHeaderName);

            // Construct builder
            var headersProc = new SoapHeaderBuilder(context.Object, logger.Object, appConfig, request);

            // Get response
            XmlDocument header = headersProc.ResponseSoapHeaders(config);

            // Assert

            Assert.IsNotNull(header);
            Assert.IsNotNull(header.DocumentElement);
            Assert.IsTrue(header.DocumentElement.LocalName.Equals("Header"));

            XmlNode n = header.DocumentElement.SelectSingleNode("*[local-name()='ResponseHeaderName1']");
            Assert.IsNotNull(n);
            Assert.IsTrue(n.Attributes[0].LocalName.Equals("AnAttribute"));
            // value as per request 
            Assert.IsTrue(n.Attributes[0].Value.Equals("SomeAttribValue"));
            Assert.IsTrue(n.FirstChild.LocalName.Equals("SomeElement"));
            // value as per request
            Assert.IsTrue(n.FirstChild.InnerText.Equals("SomeValue"));

        }

        [Test]
        public void TestWhereConfigProvidedAndRequestHasNoHeader()
        {
            var logger = new Mock<ILogger>(MockBehavior.Default);
            var context = new Mock<IExecutionContext>();
            context.Setup(x => x.EndpointName).Returns("");
            var appConfig = TestHelper.GetAppConfigData();

            // Setup test data

            bool xmlOnly = false;
            RequestSoapHeadersType config = GetConfig(xmlOnly);

            bool withHeader = false;
            string requestHeaderName = null;
            XmlDocument request = GetRequest(withHeader, requestHeaderName);

            // Construct builder
            var headersProc = new SoapHeaderBuilder(context.Object, logger.Object, appConfig, request);

            // Get response
            XmlDocument header = headersProc.ResponseSoapHeaders(config);

            // Assert
            Assert.IsNull(header);

        }

        /// <summary>
        /// Expect exception, need to act on request
        /// </summary>
        [Test]
        public void TestNoRequestDocumentProvided()
        {
            var logger = new Mock<ILogger>(MockBehavior.Default);
            var context = new Mock<IExecutionContext>();
            context.Setup(x => x.EndpointName).Returns("");
            var appConfig = TestHelper.GetAppConfigData();

            // Setup test data

            bool xmlOnly = false;
            RequestSoapHeadersType config = GetConfig(xmlOnly);

            bool withHeader = false;
            string requestHeaderName = null;
            XmlDocument request = GetRequest(withHeader, requestHeaderName);

            // Construct builder, NO REQUEST PASSED
            var headersProc = new SoapHeaderBuilder(context.Object, logger.Object, appConfig);

            // Get response
            Assert.Throws<InvalidOperationException>(() => headersProc.ResponseSoapHeaders(config));


        }

        /// <summary>
        /// As an extra test use xml and serialize into the config
        /// </summary>
        /// <param name="xmlOnly"></param>
        /// <returns></returns>
        private RequestSoapHeadersType GetConfig(bool xmlOnly)
        {
            Stream s = null;

            if (xmlOnly)
            {
                s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("MockingBird.Simulator.FunctionalTests.Xml.SoapHeaderConfiguration_XmlOnly.xml");
            }
            else
            {
                s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("MockingBird.Simulator.FunctionalTests.Xml.SoapHeaderConfiguration.xml");
            }

            System.Xml.Serialization.XmlSerializer xmlz = new System.Xml.Serialization.XmlSerializer(typeof(RequestSoapHeadersType));

            return (RequestSoapHeadersType)xmlz.Deserialize(s);
        }

        /// <summary>
        /// Get xml data from resource embedded in this test assembly
        /// </summary>
        /// <param name="withHeader"></param>
        /// <param name="requestHeaderName"></param>
        /// <returns></returns>
        private XmlDocument GetRequest(bool withHeader, string requestHeaderName)
        {
            XmlDocument request = new XmlDocument();
            string xml = null;

            if (withHeader)
            {
                xml = "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
                        "<soap:Header>" +
                            "<{0} AnAttribute=\"SomeAttribValue\">" +
                                "<SomeElement>SomeValue</SomeElement>" +
                            "</{0}>" +
                        "</soap:Header>" +
                        "<soap:Body>" +
                            "<Request/>" +
                        "</soap:Body>" +
                     "</soap:Envelope>";

                xml = System.String.Format(xml, requestHeaderName);
            }
            else
            {
                xml = "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
                        "<soap:Header/>" +
                        "<soap:Body>" +
                            "<Request/>" +
                        "</soap:Body>" +
                     "</soap:Envelope>";
            }

            request.LoadXml(xml);

            return request;
        }


        #endregion
    }
}
